package com.ohos.connect.devicecloud.iot.modular.device.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import com.ohos.connect.devicecloud.app.api.AppUserApi;
import com.ohos.connect.devicecloud.app.api.ProjectApi;
import com.ohos.connect.devicecloud.app.api.ProjectDeviceApi;
import com.ohos.connect.devicecloud.app.model.vo.AppProjectVO;
import com.ohos.connect.devicecloud.auth.core.util.StpAppLoginUserUtil;
import com.ohos.connect.devicecloud.common.exception.CommonException;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceAuthCodeDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceControlDataDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceDataInfoDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceHeartBeatDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceLeakReportDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceLoginDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceNetInfoDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DevicePskDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceRevokeDTO;
import com.ohos.connect.devicecloud.iot.model.dto.DeviceStatusSyncDTO;
import com.ohos.connect.devicecloud.iot.model.dto.deviceactive.DeviceActiveDTO;
import com.ohos.connect.devicecloud.iot.model.dto.deviceinfosync.DeviceInfoSyncDTO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceAuthCodeVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceDataVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceHeartBeatVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceInfoSyncVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceInfoVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceLeakReportVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceLoginVO;
import com.ohos.connect.devicecloud.iot.model.vo.DevicePskVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceRevokeVO;
import com.ohos.connect.devicecloud.iot.model.vo.DeviceStatusSyncVO;
import com.ohos.connect.devicecloud.iot.model.vo.deviceactive.DeviceActiveLicenseInfoVO;
import com.ohos.connect.devicecloud.iot.model.vo.deviceactive.DeviceActiveVO;
import com.ohos.connect.devicecloud.iot.modular.device.constant.DeviceConstant;
import com.ohos.connect.devicecloud.iot.modular.device.mapper.DeviceMapper;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.AppProxyDeviceInsertDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.DeviceControlDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.DeviceNameUpdateDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.DeviceRegisterCodeDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.DeviceRegisterInfoInsertDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.dto.DeviceShadeDataGetDTO;
import com.ohos.connect.devicecloud.iot.modular.device.model.entity.Device;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceAddParam;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceEditParam;
import com.ohos.connect.devicecloud.iot.modular.device.model.param.DeviceIdParam;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.DeviceRegisterCodeVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.DeviceRegisterInfoVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.shadowdata.DeviceShadowDataVO;
import com.ohos.connect.devicecloud.iot.modular.device.model.vo.shadowdata.DeviceShadowDeviceInfoVO;
import com.ohos.connect.devicecloud.iot.modular.device.service.DeviceService;
import com.ohos.connect.devicecloud.mqtt.api.CoapApi;
import com.ohos.connect.devicecloud.mqtt.api.MqttApi;
import com.ohos.connect.devicecloud.mqtt.core.model.dto.CoapToHttpDTO;
import com.ohos.connect.devicecloud.mqtt.core.model.vo.CoapToHttpVO;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.paho.mqttv5.common.MqttException;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

/**
 * 设备表Service接口实现类
 *
 * @author Dongtao
 * @date 2024/12/17 10:43
 **/
@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {

    @Resource
    private ProjectApi projectApi;

    @Resource
    private ProjectDeviceApi projectDeviceApi;

    @Resource
    private AppUserApi appUserApi;

    @Resource
    private CoapApi coapApi;

    @Resource
    private MqttApi mqttApi;

    @Value("${iot.cloudAddress}")
    private String CLOUD_ADDRESS = "connect.harmonytsc.com:7881";

    @Value("${iot.activeTime}")
    private Integer ACTIVE_TIME = 300;

    @Value("${iot.redis.prefix}")
    private String IOT_REDIS_PREFIX = "iot";

    @Resource
    private RedisTemplate<String, Object> redisTemplate;


    @Override
    public DeviceRegisterCodeVO getRegisterAuthCode(DeviceRegisterCodeDTO deviceRegisterCodeDTO) {
        //判断sn是否已经存在云平台,如果存在,将其删除,如果不存在,走正常添加逻辑
        String sn = deviceRegisterCodeDTO.getSn();
        Device device = getOne(new LambdaQueryWrapper<Device>().eq(Device::getSn, sn));
        if (ObjectUtil.isNotNull(device)) {
            String deviceId = device.getId();
            //存在该设备,删除该设备信息
            projectDeviceApi.deleteProjectDevice(deviceId);
            // 删掉设备表中设备信息
            removeById(deviceId);
            // 设备在线时,删除缓存中设备信息
            redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);
        }

        DeviceRegisterCodeVO deviceRegisterCodeVO = new DeviceRegisterCodeVO();

        // 校验projectId
        checkProjectId(deviceRegisterCodeDTO.getProjectId());

        // todo:结合场景业务云，校验productId
        if (ObjectUtil.isNull(deviceRegisterCodeDTO.getProductId())) {
            throw new CommonException("产品id不能为空");
        }

        // 生成deviceId
        String deviceId = UUID.randomUUID().toString();
        // 生成设备注册验证码，并设置过期时间
        String registerAuthCode = RandomUtil.randomNumbers(8);
        // 生成psk
        String psk = getPsk();

        // 写入redis,设置验证码过期时间
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("registerAuthCode", registerAuthCode);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("psk", psk);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("projectId", deviceRegisterCodeDTO.getProjectId());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("productId", deviceRegisterCodeDTO.getProductId());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("registryType", deviceRegisterCodeDTO.getRegistryType());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("sn", deviceRegisterCodeDTO.getSn());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("uniqueDeviceId", "".equals(deviceRegisterCodeDTO.getSn()) ? "false" : "true");
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("userId", StpAppLoginUserUtil.getAppLoginUserId());

        // 设置key过期时间
        redisTemplate.expire(IOT_REDIS_PREFIX + deviceId, ACTIVE_TIME, TimeUnit.SECONDS);

        deviceRegisterCodeVO.setDeviceId(deviceId);
        deviceRegisterCodeVO.setVerifyCode(registerAuthCode);
        deviceRegisterCodeVO.setPsk(psk);
        deviceRegisterCodeVO.setCloudAddress(CLOUD_ADDRESS);
        deviceRegisterCodeVO.setActiveTime(ACTIVE_TIME);
        deviceRegisterCodeVO.setActivated(false);

        return deviceRegisterCodeVO;
    }

    // 随机生成字符串
    private String getPsk() {
        // 生成一个16字节的随机字节数组
        byte[] randomBytes = RandomUtil.randomBytes(16);

        // 将字节数组转换为十六进制字符串
        StringBuilder hexString = new StringBuilder();
        for (byte b : randomBytes) {
            // 将每个字节转换为两位十六进制数，并追加到StringBuilder中
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0'); // 如果只有一个字符，前面补0
            }
            hexString.append(hex);
        }

        // 输出生成的十六进制字符串
        System.out.println("PSK =====> " + hexString.toString());
        return hexString.toString();
    }


    @Override
    public Boolean getDeviceActiveStatus(String projectId, String devId) {
        if (ObjectUtil.isNull(projectId)) {
            throw new CommonException("项目id不能为空");
        }

        if (ObjectUtil.isNull(devId)) {
            throw new CommonException("设备id不能为空");
        }
        checkProjectId(projectId);

        Device device = getById(devId);
        if (ObjectUtil.isNull(device)) {
            throw new CommonException("设备不存在");
        }

        if (ObjectUtil.isNull(device.getActivated()) || !device.getActivated()) {
            return Boolean.FALSE;
        }
        return Boolean.TRUE;
    }

    @Override
    public DeviceRegisterInfoVO appRegisterDevice(DeviceRegisterInfoInsertDTO deviceRegisterInfoInsertDTO) {
        DeviceRegisterInfoVO deviceRegisterInfoVO = new DeviceRegisterInfoVO();
        checkProjectId(deviceRegisterInfoInsertDTO.getProjectId());

        AppProxyDeviceInsertDTO deviceInsertDTO = deviceRegisterInfoInsertDTO.getDevInfo();
        //随机密码
        String password = RandomUtil.randomString(32);
        // 生成deviceId
        String deviceId = UUID.randomUUID().toString();

        Device device = BeanUtil.copyProperties(deviceInsertDTO, Device.class);
        device.setId(deviceId);
        device.setDeviceType(deviceInsertDTO.getDevType());
        device.setProductId(deviceInsertDTO.getProdId());
        device.setModelData(JSON.toJSONString(deviceRegisterInfoInsertDTO.getServices()));
        device.setPassword(password);
        // 持久化存储设备信息到数据库
        boolean saveDevice = save(device);
        if (Boolean.FALSE.equals(saveDevice)) {
            throw new CommonException("保存设备信息失败");
        }
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("projectId", deviceRegisterInfoInsertDTO.getProjectId());
        //添加到项目中
        projectDeviceApi.insertProjectDevice(deviceRegisterInfoInsertDTO.getProjectId(), deviceId);
        deviceRegisterInfoVO.setDevId(deviceId);
        return deviceRegisterInfoVO;
    }

    @Transactional
    @Override
    public Boolean changeDeviceName(String projectId, String deviceId, DeviceNameUpdateDTO deviceNameUpdateDTO) {
        checkProjectId(projectId);

        if (ObjectUtil.isNull(deviceId)) {
            throw new CommonException("设备id不能为空");
        }
        Device device = getById(deviceId);
        if (ObjectUtil.isNull(device)) {
            throw new CommonException("未查询到设备");
        }

        device.setName(deviceNameUpdateDTO.getName());
        return saveOrUpdate(device);
    }

    @Transactional
    @Override
    public Boolean deleteDevice(String projectId, String deviceId) {

        checkProjectId(projectId);

        // 从项目中，移除设备
        Boolean result1 = projectDeviceApi.deleteProjectDevice(deviceId);

        // 删掉设备表中设备信息
        Boolean result2 = removeById(deviceId);
        // 向APP发送设备删除通知
        sendDeviceDeleteNoticeToApp("", deviceId, appUserApi.getUserAccountById(StpAppLoginUserUtil.getAppLoginUserId()));
        Boolean result3 = true;
        //判断设备是否在线
        if (Boolean.TRUE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))) {
            String heartBeat = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("heartBeat");
            if (ObjectUtil.isNotNull(heartBeat)) {
                Long heartTime = Long.parseLong(heartBeat);
                long currentTime = System.currentTimeMillis();
                if (currentTime - heartTime > 90 * 1000) {
                    //设备已经离线
                    //将设备离线信息放到缓存中
                    redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("state", "offline");
                    System.out.println("当前是设备离线流程>>>>>>>>>>>>>>>>>>>>>>>1");
                } else {
                    // 设备在线时,删除缓存中设备信息
                    result3 = redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);
                    System.out.println("当前是设备在线流程>>>>>>>>>>>>>>>>>>>>>>>2");
                }
            } else {
                //将设备离线信息放到缓存中
                redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("state", "offline");
                System.out.println("当前是没有获取到设备心跳数据流程>>>>>>>>>>>>>>>>>>>>>>>3");
            }
        }
        // 向设备发送设备删除通知
        sendDeviceDeleteNoticeToDevice(deviceId);


        return result1 && result2 && result3;
    }

    @Override
    public Boolean deviceControl(DeviceControlDTO deviceControlDTO) {
        //JSONObject servicesObj = deviceControlDTO.getServices();
        List<JSONObject> serviceList = (List<JSONObject>) deviceControlDTO.getServices();

        ArrayList<HashMap<String, Object>> service = new ArrayList<>();
        // 拼装CoapToHttpDTO
        CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
        for (int i = 0; i < serviceList.size(); i++) {
            JSONObject obj = new JSONObject(serviceList.get(i));
            if (ObjectUtil.isEmpty(obj)) {
                continue;
            }
            String st = obj.get("st").toString();
            deviceControlDTO.setServices(obj);
            if (ObjectUtil.isEmpty(obj.get("sid"))) {
                continue;
            }
            coapToHttpDTO.setUrl(obj.get("sid").toString());
            coapToHttpDTO.setMethod("POST");
            if (StringUtils.equals(obj.get("sid").toString(), "devInfo")) {
                coapToHttpDTO.setMethod("GET");
            }
            HashMap<String, Object> body = new HashMap<>();
            body.put("st", st);
            body.put("data", obj.get("data"));
            service.add(body);
        }
        //获取设备信息
        Device device = getById(deviceControlDTO.getDevId());

        coapToHttpDTO.setBody(JSON.toJSONString(service));
        HashMap<String, Object> option = new HashMap<>();
        option.put("REQ_ID", RandomUtil.randomString(10));
        option.put("DEV_ID", deviceControlDTO.getDevId());
        if (StringUtils.isNotBlank(device.getGatewayId())) {
            option.put("GATEWAY_ID", device.getGatewayId());
        }
        option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
        option.put("SEQ_NUM_ID", RandomUtil.randomNumbers(5));
        coapToHttpDTO.setOption(option);
        CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);
        return Boolean.TRUE;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean RestoreFactory(DeviceControlDTO deviceControlDTO) {
        List<JSONObject> serviceList = (List<JSONObject>) deviceControlDTO.getServices();
        JSONObject obj = new JSONObject(serviceList.get(0));
        deviceControlDTO.setServices(obj);
        // 拼装CoapToHttpDTO
        CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
        coapToHttpDTO.setUrl(obj.get("sid").toString());
        coapToHttpDTO.setMethod("POST");
        coapToHttpDTO.setBody(JSON.toJSONString(deviceControlDTO));
        HashMap<String, Object> option = new HashMap<>();
        option.put("REQ_ID", RandomUtil.randomString(10));
        option.put("DEV_ID", deviceControlDTO.getDevId());
        option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
        option.put("SEQ_NUM_ID", RandomUtil.randomNumbers(5));
        coapToHttpDTO.setOption(option);

        //发送coap信息到设备端
        CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);

        //发送coap协议后删除设备信息
        String deviceId = deviceControlDTO.getDevId();
        // 从项目中，移除设备
        Boolean result1 = projectDeviceApi.deleteProjectDevice(deviceId);
        // 删掉设备表中设备信息
        Boolean result2 = removeById(deviceId);
        // 删除缓存中设备信息
        Boolean result3 = redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);


        return coapToHttpVO.getIsSuccess() && result1 && result2 && result3;
    }

    @Override
    public List<DeviceAuthCodeVO> getDeviceAuthCodeList(List<DeviceAuthCodeDTO> deviceAuthCodeDTOList) {
        List<DeviceAuthCodeVO> deviceAuthCodeVOList = new ArrayList<>();
        for (DeviceAuthCodeDTO deviceAuthCodeDTO : deviceAuthCodeDTOList) {
            DeviceAuthCodeVO deviceAuthCodeVO = new DeviceAuthCodeVO();
            deviceAuthCodeVO = deviceAuthCode(deviceAuthCodeDTO);
            deviceAuthCodeVOList.add(deviceAuthCodeVO);
        }
        return deviceAuthCodeVOList;
    }

    @Override
    public DeviceShadowDataVO getDeviceShadowData(DeviceShadeDataGetDTO deviceShadeDataGetDTO) {
        DeviceShadowDataVO deviceShadowDataVO = new DeviceShadowDataVO();
        String deviceId = deviceShadeDataGetDTO.getDevid();
        checkProjectId(deviceShadeDataGetDTO.getProjectId());

        if (Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))) {
            throw new CommonException("设备不存在，设备id: " + deviceId);
        }
        JSONArray modelData = null;
        if (StringUtils.isNotBlank((String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("modelData"))) {
            log.error((String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("modelData"));
            modelData = JSONUtil.parseArray((String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("modelData"));
        }
        if (ObjectUtil.isEmpty(modelData)) {
            return deviceShadowDataVO;
        }

        Device device = getById(deviceId);
        DeviceShadowDeviceInfoVO deviceInfoVO = BeanUtil.copyProperties(device, DeviceShadowDeviceInfoVO.class);

        deviceShadowDataVO.setDevId(deviceId);
        deviceShadowDataVO.setGatewayId("");
        deviceShadowDataVO.setNoteType("noteType");
        deviceShadowDataVO.setServices(modelData);
        deviceShadowDataVO.setDevInfo(deviceInfoVO);

        return deviceShadowDataVO;
    }

    @Override
    public String getDeviceAllShadowData(DeviceShadeDataGetDTO deviceShadeDataGetDTO) {
        Device device = getById(deviceShadeDataGetDTO.getDevid());
        if (ObjectUtil.isEmpty(device)) {
            throw new CommonException("设备表不存在，id值为：{}", deviceShadeDataGetDTO.getDevid());
        }
        ArrayList<HashMap<String, Object>> service = new ArrayList<>();
        //向设备发送请求详情的命令
        String sid = "allServices";
        String st = "allServices";
        HashMap<String, Object> body = new HashMap<>();
        HashMap<String, String> data = new HashMap<>();
        body.put("st", st);
        body.put("data", data);
        service.add(body);

        CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
        coapToHttpDTO.setUrl(sid);
        coapToHttpDTO.setMethod("GET");
        coapToHttpDTO.setBody(JSON.toJSONString(service));
        HashMap<String, Object> option = new HashMap<>();
        option.put("REQ_ID", RandomUtil.randomString(10));
        option.put("DEV_ID", deviceShadeDataGetDTO.getDevid());
        option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
        option.put("SEQ_NUM_ID", RandomUtil.randomNumbers(5));
        coapToHttpDTO.setOption(option);
        // 获取的详情
        CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);

        if (ObjectUtil.isNotNull(coapToHttpVO) && StringUtils.isNotBlank(coapToHttpVO.getBody())) {
            return coapToHttpVO.getBody();
        }
        return "";
    }

    @Override
    public DevicePskVO exchangeDeviceSn(DevicePskDTO devicePskDTO) {
        String deviceId = devicePskDTO.getDevId();
        // 校验设备id，设备注册验证码是否失效
        if (Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))) {
            throw new CommonException("设备id错误或者设备注册验证码失效");
        }

        String state = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("state");
        System.out.println("psk流程获取设备离线状态>>>>>>>>>>>>>" + state);
        if (ObjectUtil.isNotNull(state) && "offline".equals(state)) {
            DevicePskVO devicePskVO = new DevicePskVO();
            devicePskVO.setSn2(getRandomSn());
            devicePskVO.setSeq(devicePskDTO.getSeq() + 1);
            devicePskVO.setEncryptMode(DeviceConstant.PLAIN_TEXT);
            devicePskVO.setErrcode(0);
            redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("sn2", devicePskVO.getSn2());
            redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("encryption", devicePskVO.getEncryptMode());
            return devicePskVO;
        }
        // 随机生成sn2
        String sn2 = getRandomSn();
        DevicePskVO devicePskVO = new DevicePskVO();
        devicePskVO.setSn2(sn2);
        devicePskVO.setSeq(devicePskDTO.getSeq() + 1);
        devicePskVO.setEncryptMode(DeviceConstant.PLAIN_TEXT);
        devicePskVO.setErrcode(0);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("sn2", devicePskVO.getSn2());
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("encryption", devicePskVO.getEncryptMode());

        return devicePskVO;
    }

    private String getRandomSn() {
        // 生成8字节的随机数据
        byte[] randomBytes = RandomUtil.randomBytes(8);

        // 将字节数组转换成十六进制字符串
        // Hutool没有直接提供字节到十六进制字符串的转换方法，所以我们使用Java标准库
        StringBuilder hexString = new StringBuilder(2 * randomBytes.length); // 每个字节变成两个十六进制字符
        for (byte b : randomBytes) {
            // 将每个字节转换成两位的十六进制数并追加到StringBuilder中
            hexString.append(String.format("%02x", b & 0xff));
        }

        return hexString.toString();
    }

    @Transactional
    @Override
    public DeviceActiveVO activeDevice(DeviceActiveDTO deviceActiveDTO) {
        DeviceActiveVO deviceActiveVO = new DeviceActiveVO();

        // 判断注册码是否过期
        String deviceId = deviceActiveDTO.getDevId();
        if (Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))) {
//            throw new CommonException("设备id错误或者设备注册验证码失效");
            deviceActiveVO.setErrcode(1);
            return deviceActiveVO;
        }

        // 校验注册码
        String rightCode = Objects.requireNonNull(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("registerAuthCode")).toString();
        if (!rightCode.equals(deviceActiveDTO.getCode())) {
//            throw new CommonException("设备注册验证码错误");
            deviceActiveVO.setErrcode(2);
            return deviceActiveVO;
        }

        // 判断productId是否吻合
        String rightProductId = Objects.requireNonNull(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("productId")).toString();
        if (!rightProductId.equals(deviceActiveDTO.getDevInfo().getProdId())) {
//            throw new CommonException("设备产品不匹配");
            deviceActiveVO.setErrcode(3);
            return deviceActiveVO;
        }

        // 判断SN是否需要校验
        if (Objects.equals(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("uniqueDeviceId"), true)) {
            if (!Objects.equals(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("sn"), deviceActiveDTO.getDevInfo().getSn())) {
//                throw new CommonException("设备Sn不匹配");
                deviceActiveVO.setErrcode(6);
                return deviceActiveVO;
            }
        }

        // 校验sn是否已被注册
        if (isDeviceSnExist(deviceActiveDTO.getDevInfo().getSn())) {
//                throw new CommonException("设备已被注册");
            deviceActiveVO.setErrcode(4);
            return deviceActiveVO;
        }

        // 校验设备ID在设备表中是否已存在
        if (isDeviceIdExist(deviceId)) {
//            throw new CommonException("设备ID已存在，请勿重复激活");
            deviceActiveVO.setErrcode(5);
            return deviceActiveVO;
        }


        // 将Redis中的设备信息持久化
        Boolean persistDeviceInfo = redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).persist();
        if (Boolean.FALSE.equals(persistDeviceInfo)) {
//            throw new CommonException("Redis持久化该设备信息失败，deviceId : " + deviceId);
            deviceActiveVO.setErrcode(6);
            return deviceActiveVO;
        }

        // 生成设备密码
        String password = RandomUtil.randomString(32);
        String licenseId = UUID.randomUUID().toString();
        String license = RandomUtil.randomNumbers(32);

        // 插入到设备表
        Device device = BeanUtil.toBean(deviceActiveDTO.getDevInfo(), Device.class);
        device.setName(deviceActiveDTO.getDevInfo().getDevName());
        device.setProductId(deviceActiveDTO.getDevInfo().getProdId());
        device.setId(deviceId);
        device.setPassword(password);
        device.setLicenseId(licenseId);
        device.setLicense(license);
        device.setActivated(true);
        boolean addResult = save(device);
        if (!addResult) {
            throw new CommonException("保存设备激活信息失败");
        }

        // 插入项目设备表
        projectDeviceApi.insertProjectDevice(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("projectId").toString(), deviceId);


        deviceActiveNotice(redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("projectId").toString(), deviceId, device);

        deviceActiveVO.setErrcode(0);
        deviceActiveVO.setDevId(device.getId());
        deviceActiveVO.setSecret(device.getPassword());

        DeviceActiveLicenseInfoVO deviceActiveLicenseInfoVO = new DeviceActiveLicenseInfoVO();
        deviceActiveLicenseInfoVO.setUuid(licenseId);
        deviceActiveLicenseInfoVO.setLicense(license);

        deviceActiveVO.setLicenseInfo(deviceActiveLicenseInfoVO);

        return deviceActiveVO;
    }

    // todo:向APP发送设备激活MQTT通知
    private void deviceActiveNotice(String projectId, String devId, Device device) {
        String topic = "/user/" + getDeviceUserAccount(devId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "bindDevice");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("resultCode", "succeeded");
        body.put("devId", devId);
        body.put("projectId", projectId);
        body.put("verifyCodeDevId", "");

        JSONObject devObj = new JSONObject();

        devObj.put("sn", device.getSn());
        devObj.put("model", device.getModel());
        devObj.put("devType", device.getDeviceType());
        devObj.put("manu", device.getManu());
        devObj.put("mac", device.getMac());
        devObj.put("hiv", device.getHiv());
        devObj.put("fwv", device.getFwv());
        devObj.put("hwv", device.getHwv());
        devObj.put("swv", device.getSwv());
        devObj.put("protType", "");
        devObj.put("prodId", device.getProductId());

        body.put("devInfo", devObj);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic, message);
        } catch (MqttException e) {
            e.printStackTrace();
        }


    }

    @Override
    public DeviceLoginVO loginDevice(DeviceLoginDTO deviceLoginDTO) {
        DeviceLoginVO deviceLoginVO = new DeviceLoginVO();

        String deviceId = deviceLoginDTO.getDevId();

        Device device = getById(deviceId);

        // 判断设备是否存在
        if (ObjectUtil.isNull(device)) {
//            throw new CommonException("设备不存在，设备id: " + deviceId);
            deviceLoginVO.setErrcode(6);
            // 删除缓存中设备信息
            redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);
            return deviceLoginVO;
        }

        // 校验密码
        if (Boolean.FALSE.equals(device.getPassword().equals(deviceLoginDTO.getSecret()))) {
//            throw new CommonException("密码错误");
            deviceLoginVO.setErrcode(1);
            return deviceLoginVO;
        }

        // 生成accessToken  refreshToken
        String accessToken = UUID.randomUUID().toString();
        String refreshToken = UUID.randomUUID().toString();

        // 更新到redis中
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + "token:" + deviceId).put("accessToken", accessToken);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + "token:" + deviceId).put("refreshToken", refreshToken);

        // 发送设备在线通知
        deviceOnlineNotice("", deviceId);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("heartBeat", Long.toString(System.currentTimeMillis()));
        //redis状态更新为在线
        checkStatus(deviceId);

        deviceLoginVO.setErrcode(0);
        deviceLoginVO.setTimeout(600);
        deviceLoginVO.setAccessToken(accessToken);
        deviceLoginVO.setRefreshToken(refreshToken);
        deviceLoginVO.setTimeout(600);
        deviceLoginVO.setTimestamp(Long.toString(System.currentTimeMillis()));
        deviceLoginVO.setTimezone("Asia/Shanghai");
        deviceLoginVO.setPeriod("600");

        // todo：登录成功后，开启设备心跳

        return deviceLoginVO;
    }

    // 向APP发送设备在线通知   网关id预留
    private void deviceOnlineNotice(String gatewayId, String devId) {
        String topic = "/user/" + getDeviceUserAccount(devId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceStatus");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", devId);
        body.put("gatewayId", gatewayId);
        body.put("status", "online");

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic, message);
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }

    @Transactional
    @Override
    public DeviceInfoSyncVO syncDeviceInfo(List<DeviceInfoSyncDTO> deviceInfoSyncDTOList) {
        DeviceInfoSyncVO deviceInfoSyncVO = new DeviceInfoSyncVO();

        if (ObjectUtil.isEmpty(deviceInfoSyncDTOList)) {
            //throw new CommonException("设备信息不能为空");
            deviceInfoSyncVO.setErrcode(1);
            return deviceInfoSyncVO;
        }

        for (DeviceInfoSyncDTO deviceInfoSyncDTO : deviceInfoSyncDTOList) {
            int errCode = saveDeviceBasicInfo(deviceInfoSyncDTO);
            if (errCode != 0) {
                deviceInfoSyncVO.setErrcode(errCode);
                return deviceInfoSyncVO;
            }
        }
        deviceInfoSyncVO.setErrcode(0);
        return deviceInfoSyncVO;
    }

    // 更新设备基本信息到数据库
    @Transactional
    public int saveDeviceBasicInfo(DeviceInfoSyncDTO deviceInfoSyncDTO) {
        String deviceId = deviceInfoSyncDTO.getDevId();
        String gatewayId = deviceInfoSyncDTO.getGatewayId();
        Device device = getById(deviceId);

        // 判断设备是否存在
        if (ObjectUtil.isNull(device)) {
//            throw new CommonException("设备不存在，设备id: " + deviceId);
            return 2;
        }

        // 更新数据库中，设备的基本信息
        //判断设备端是否上传设备信息
        if (ObjectUtil.isNotEmpty(deviceInfoSyncDTO.getDevInfo())) {
            device = BeanUtil.toBean(deviceInfoSyncDTO.getDevInfo(), Device.class);
            device.setName(deviceInfoSyncDTO.getDevInfo().getDevName());
        }
        device.setId(deviceId);
        device.setGatewayId(gatewayId);
        device.setModelData(deviceInfoSyncDTO.getServices().toString());
        Boolean updateResult = updateById(device);
        if (Boolean.FALSE.equals(updateResult)) {
            throw new CommonException("更新设备数据失败");
        }

        return 0;
    }

    @Override
    public DeviceDataVO deviceDataUpload(List<DeviceDataInfoDTO> deviceDataInfoDTOList) {
        DeviceDataVO deviceDataVO = new DeviceDataVO();
        if (ObjectUtil.isEmpty(deviceDataInfoDTOList)) {
            deviceDataVO.setErrcode(1);
            return deviceDataVO;
        }

        for (DeviceDataInfoDTO deviceDataInfoDTO : deviceDataInfoDTOList) {
            int errCode = storeDeviceDataToCache(deviceDataInfoDTO);
            if (errCode != 0) {
                deviceDataVO.setErrcode(errCode);
                return deviceDataVO;
            }
        }

        deviceDataVO.setErrcode(0);
        return deviceDataVO;

    }

    @Override
    public DeviceDataVO deviceAllShadowDataUpload(List<DeviceNetInfoDTO> deviceDataInfoDTOList) {
        DeviceDataVO deviceDataVO = new DeviceDataVO();
        if (ObjectUtil.isEmpty(deviceDataInfoDTOList)) {
            deviceDataVO.setErrcode(1);
            return deviceDataVO;
        }

        for (DeviceNetInfoDTO deviceDataInfoDTO : deviceDataInfoDTOList) {
            int errCode = storeDeviceAllShadowDataToCache(deviceDataInfoDTO);
            if (errCode != 0) {
                deviceDataVO.setErrcode(errCode);
                return deviceDataVO;
            }
        }

        deviceDataVO.setErrcode(0);
        return deviceDataVO;
    }

    @Override
    public DeviceDataVO deviceNetInfoDataUpload(List<DeviceNetInfoDTO> deviceDataInfoDTOList) {
        DeviceDataVO deviceDataVO = new DeviceDataVO();
        if (ObjectUtil.isEmpty(deviceDataInfoDTOList)) {
            deviceDataVO.setErrcode(1);
            return deviceDataVO;
        }

        for (DeviceNetInfoDTO deviceDataInfoDTO : deviceDataInfoDTOList) {
            int errCode = storeDeviceNetInfoDataToCache(deviceDataInfoDTO);
            if (errCode != 0) {
                deviceDataVO.setErrcode(errCode);
                return deviceDataVO;
            }
        }

        deviceDataVO.setErrcode(0);
        return deviceDataVO;

    }

    @Override
    public DeviceDataVO deviceControlReturn(DeviceControlDataDTO deviceControlDataDTO) {
        DeviceDataVO deviceDataVO = new DeviceDataVO();
        String devId = deviceControlDataDTO.getDevId();
        String sid = deviceControlDataDTO.getSid();
        String errcode = deviceControlDataDTO.getErrcode();
        HashMap<String, Object> serviceData = new HashMap<>();
        serviceData.put("sid", sid);
        serviceData.put("errcode", errcode);

        System.out.println("控制设备返回值的信息:" + JSON.toJSONString(serviceData));

        // 发送MQTT控制设备返回值通知
        try {
            sendDeviceDataChanged(devId, "", serviceData);
        } catch (MqttException e) {
            e.printStackTrace();
        }
        return deviceDataVO;
    }


    // 存储设备服务数据到缓存中，并发送mqtt通知
    private int storeDeviceDataToCache(DeviceDataInfoDTO deviceDataInfoDTO) {
        int errCode = 0;
        String deviceId = deviceDataInfoDTO.getDevId();
        Device device = getById(deviceId);
        if (ObjectUtil.isNull(device)) {
            return 2;
        }

        // 保存设备服务数据到缓存中
        JSONArray serviceInfoList = new JSONArray(device.getModelData());
        JSONArray servicesDataList = new JSONArray(deviceDataInfoDTO.getServices());
        for (Object serviceObj : servicesDataList) {
            JSONObject serviceDataObj = new JSONObject(serviceObj.toString());
            String sid = serviceDataObj.get("sid").toString();
            if ("allServices".equals(sid)) {
                continue;
            }
            for (Object serviceInfoObj : serviceInfoList) {
                JSONObject serviceInfo = new JSONObject(serviceInfoObj.toString());
                if (sid.equals(serviceInfo.get("sid").toString())) {
                    serviceDataObj.put("data", serviceInfo.get("data"));
                    serviceDataObj.remove("reportId");
                }

            }

        }
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("modelData", servicesDataList.toString());
        // 发送MQTT设备状态变化通知
        try {
            sendDeviceDataChanged(deviceId, "", servicesDataList);
        } catch (MqttException e) {
            e.printStackTrace();
        }

        return errCode;
    }

    // 存储设备快照数据到缓存中，并发送mqtt通知
    private int storeDeviceAllShadowDataToCache(DeviceNetInfoDTO deviceDataInfoDTO) {
        int errCode = 0;
        String deviceId = deviceDataInfoDTO.getDevId();
        Device device = getById(deviceId);
        if (ObjectUtil.isNull(device)) {
            return 2;
        }
        JSONArray servicesNetInfo = new JSONArray(deviceDataInfoDTO.getData().toString());

        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("allServices", servicesNetInfo.toString());
        // 发送MQTT设备状态变化通知
        try {
            sendDeviceServicesChanged(deviceId, "", servicesNetInfo, "DeviceAllServicesChanged");
        } catch (MqttException e) {
            e.printStackTrace();
        }

        return errCode;
    }


    // 存储设备网络信息服务数据到缓存中，并发送mqtt通知
    private int storeDeviceNetInfoDataToCache(DeviceNetInfoDTO deviceDataInfoDTO) {
        int errCode = 0;
        String deviceId = deviceDataInfoDTO.getDevId();
        Device device = getById(deviceId);
        if (ObjectUtil.isNull(device)) {
            return 2;
        }
        JSONObject servicesNetInfo = new JSONObject(deviceDataInfoDTO.getData().toString());
        // 发送MQTT设备状态变化通知
        try {
            sendDeviceDataChanged(deviceId, "", servicesNetInfo);
        } catch (MqttException e) {
            e.printStackTrace();
        }
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("devNetInfo", servicesNetInfo.toString());
        return errCode;
    }

    // 向APP发送设备数据变化通知
    private void sendDeviceDataChanged(String deviceId, String gatewayId, Object servicesData) throws MqttException {

        String topic = "/user/" + getDeviceUserAccount(deviceId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("requestId", RandomUtil.randomString(8));
        header.put("from", "/na/dataService");
        header.put("to", "/app");
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceDataChanged");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", deviceId);
        body.put("gatewayId", gatewayId);
        body.put("services", servicesData);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        mqttApi.sendMqttMessage(topic, message);
    }

    //向App发送mqtt信息通用方法
    private void sendDeviceServicesChanged(String deviceId, String gatewayId, Object servicesData, String notifyType) throws MqttException {

        String topic = "/user/" + getDeviceUserAccount(deviceId) + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("requestId", RandomUtil.randomString(8));
        header.put("from", "/na/allService");
        header.put("to", "/app");
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", notifyType);

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", deviceId);
        body.put("gatewayId", gatewayId);
        body.put("services", servicesData);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        mqttApi.sendMqttMessage(topic, message);
    }

    private String getDeviceUserAccount(String deviceId) {
        return projectDeviceApi.getUserAccountByDeviceId(deviceId);
    }

    @Transactional
    @Override
    public DeviceRevokeVO deviceRevoke(DeviceRevokeDTO deviceRevokeDTO) {
        DeviceRevokeVO deviceRevokeVO = new DeviceRevokeVO();
        String deviceId = deviceRevokeDTO.getDevId();
        if (Boolean.FALSE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + deviceId))) {
//            throw new CommonException("设备id不存在");
            deviceRevokeVO.setErrcode(1);
            return deviceRevokeVO;
        }

        // 清除Redis中的设备缓存信息
        Boolean clearCache = clearDeviceCache(deviceId);
        if (Boolean.FALSE.equals(clearCache)) {
//            throw new CommonException("清除设备缓存信息失败");
            deviceRevokeVO.setErrcode(2);
            return deviceRevokeVO;
        }

        // 清除iot_device表记录
        Boolean clearDevice = removeById(deviceId);
        if (Boolean.FALSE.equals(clearDevice)) {
//            throw new CommonException("清除设备记录失败");
            deviceRevokeVO.setErrcode(3);
            return deviceRevokeVO;
        }

        // 清除app_project_device记录
        Boolean clearProject = projectDeviceApi.deleteProjectDevice(deviceId);
        if (Boolean.FALSE.equals(clearProject)) {
//            throw new CommonException("清除项目设备失败");
            deviceRevokeVO.setErrcode(4);
            return deviceRevokeVO;
        }

        // 向APP发送设备删除通知(网关设备待补充)
        sendDeviceDeleteNoticeToApp("", deviceId, getDeviceUserAccount(deviceId));


        deviceRevokeVO.setErrcode(0);
        return deviceRevokeVO;
    }

    // todo:向设备发送设备删除通知
    private void sendDeviceDeleteNoticeToDevice(String deviceId) {


        String sid = "delDevice";
        DeviceControlDTO deviceControlDTO = new DeviceControlDTO();
        deviceControlDTO.setDevId(deviceId);
        deviceControlDTO.setTimestamp(getTimezone());
        deviceControlDTO.setRequestId(RandomUtil.randomString(10));


        HashMap<String, Object> service = new HashMap<>();
        HashMap<String, Object> data = new HashMap<>();
        data.put("delDevice", true);
        service.put("sid", sid);
        service.put("data", data);
        deviceControlDTO.setServices(service);

        CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
        coapToHttpDTO.setUrl("/.sys/delDevice");
        coapToHttpDTO.setMethod("POST");

        coapToHttpDTO.setBody(JSON.toJSONString(deviceControlDTO));

        HashMap<String, Object> option = new HashMap<>();
        option.put("REQ_ID", RandomUtil.randomString(10));
        option.put("DEV_ID", deviceId);
        option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
        option.put("SEQ_NUM_ID", RandomUtil.randomNumbers(5));
        coapToHttpDTO.setOption(option);

        CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);
    }

    // 向APP发送设备添加通知
    private void sendDeviceAddNoticeToApp(String projectId, String devId, Device device) {
        String topic = "/user/" + getDeviceUserAccount(devId) + "/signaltrans/v1/categories/data";
        // 构造header
        JSONObject header = new JSONObject();
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "bindDevice");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("resultCode", "succeeded");
        body.put("devId", devId);
        body.put("projectId", projectId);
        body.put("verifyCodeDevId", "");

        JSONObject devObj = new JSONObject();

        devObj.put("sn", device.getSn());
        devObj.put("model", device.getModel());
        devObj.put("devType", device.getDeviceType());
        devObj.put("manu", device.getManu());
        devObj.put("mac", device.getMac());
        devObj.put("hiv", device.getHiv());
        devObj.put("fwv", device.getFwv());
        devObj.put("hwv", device.getHwv());
        devObj.put("swv", device.getSwv());
        devObj.put("protType", "");
        devObj.put("prodId", device.getProductId());

        body.put("devInfo", devObj);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic, message);
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }

    // 向APP发送设备删除通知
    private void sendDeviceDeleteNoticeToApp(String gatewayId, String deviceId, String appUserAccount) {
        String topic = "/user/" + appUserAccount + "/signaltrans/v1/categories/data";

        // 构造header
        JSONObject header = new JSONObject();
        header.put("requestId", RandomUtil.randomString(8));
        header.put("from", "/na/dataService");
        header.put("to", "/app");
        header.put("timestamp", Long.toString(System.currentTimeMillis()));
        header.put("category", "device");
        header.put("notifyType", "deviceDeleted");

        // 构造body
        JSONObject body = new JSONObject();
        body.put("devId", deviceId);
        body.put("gatewayId", gatewayId);

        JSONObject payload = new JSONObject();
        payload.put("header", header);
        payload.put("body", body);

        // 使用Gson将对象序列化为JSON字符串
        Gson gson = new Gson();
        String jsonPayload = gson.toJson(payload);

        // 创建MqttMessage并设置其内容
        MqttMessage message = new MqttMessage(jsonPayload.getBytes());

        try {
            mqttApi.sendMqttMessage(topic, message);
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }

    @Override
    public DeviceAuthCodeVO deviceAuthCode(DeviceAuthCodeDTO deviceAuthCodeDTO) {
        DeviceAuthCodeVO deviceAuthCodeVO = new DeviceAuthCodeVO();
        String deviceId = deviceAuthCodeDTO.getDevId();
        //判断该设备是否已注册,校验设备ID是否合法
        Device device = getById(deviceId);
        if (ObjectUtil.isNull(device)) {
            deviceAuthCodeVO.setErrcode(2);
            return deviceAuthCodeVO;
        }

        String authCode = device.getAuthCode();
        String authCodeId = device.getAuthCodeId();
        if (StrUtil.isEmpty(authCode)) {
            // 随机生成authCode 和  authCodeId
            authCode = getRandomCode();
            authCodeId = getRandomCode();
            //将生成的authCode和authCodeId持久化到数据库
            device.setAuthCode(authCode);
            device.setAuthCodeId(authCodeId);
            saveOrUpdate(device);
        }
        //缓存到redis中
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("authCode", authCode);
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).put("authCodeId", authCodeId);

        deviceAuthCodeVO.setAuthCode(authCode);
        deviceAuthCodeVO.setAuthCodeId(authCodeId);
        deviceAuthCodeVO.setTimeout(86400);
        deviceAuthCodeVO.setErrcode(0);
        deviceAuthCodeVO.setDevId(deviceId);

        return deviceAuthCodeVO;
    }

    @Override
    public List<DeviceStatusSyncVO> deviceStatusSync(List<DeviceStatusSyncDTO> deviceStatusSyncDTOList) {
        List<DeviceStatusSyncVO> deviceStatusSyncVOList = new ArrayList<>();

        for (DeviceStatusSyncDTO deviceStatusSyncDTO : deviceStatusSyncDTOList) {
            DeviceStatusSyncVO deviceStatusSyncVO = new DeviceStatusSyncVO();
            String deviceId = deviceStatusSyncDTO.getDevId();
            //判断该设备是否已注册,校验设备ID是否合法
            Device device = getById(deviceId);
            if (ObjectUtil.isNull(device)) {
                //设备不存在
                deviceStatusSyncVO.setErrcode(2);
                deviceStatusSyncVO.setDevId(deviceId);
                deviceStatusSyncVOList.add(deviceStatusSyncVO);
            } else {
                //设备状态更新正常
                deviceStatusSyncVO.setErrcode(0);
                deviceStatusSyncVO.setDevId(deviceId);
                //获取设备状态
                String status = deviceStatusSyncDTO.getStatus();
                //修改设备的对应状态信息
                device.setStatus(status);
                Boolean updateResult = updateById(device);
                if (Boolean.FALSE.equals(updateResult)) {
                    //设备状态更新失败
                    deviceStatusSyncVO.setErrcode(3);
                    deviceStatusSyncVO.setDevId(deviceId);
                }
                deviceStatusSyncVOList.add(deviceStatusSyncVO);
                System.out.println("将详细添加到返回值中:" + deviceId);
                //更新成功后发送MQTT消息到APP端
                if (updateResult) {
                    System.out.println("向App发送MQTT消息:" + deviceId);
                    String projectId = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + deviceId).get("projectId");
                    sendDeviceAddNoticeToApp(projectId, deviceId, device);
                }
            }
        }
        return deviceStatusSyncVOList;
    }

    @Override
    public DeviceLeakReportVO deviceLeakReport(DeviceLeakReportDTO deviceLeakReportDTO) {
        DeviceLeakReportVO deviceLeakReportVO = new DeviceLeakReportVO();
        String devId = deviceLeakReportDTO.getDevId();
        String gatewayId = deviceLeakReportDTO.getGatewayId();
        //收到燃气泄漏信息处理逻辑
        Object services = deviceLeakReportDTO.getServices();

        try {
            //发送mqtt消息到app
            sendDeviceServicesChanged(devId, gatewayId, services, "LeakReport");
        } catch (MqttException e) {
            e.printStackTrace();
        }
        deviceLeakReportVO.setErrcode(0);
        deviceLeakReportVO.setDevId(devId);
        return deviceLeakReportVO;
    }

    @Override
    public DeviceHeartBeatVO deviceHeartBeat(DeviceHeartBeatDTO deviceHeartBeatDTO) {
        String devId = deviceHeartBeatDTO.getDevId();
        DeviceHeartBeatVO deviceHeartBeatVO = new DeviceHeartBeatVO();
        deviceHeartBeatVO.setTimestamp(Long.toString(System.currentTimeMillis()));
        deviceHeartBeatVO.setTimestamp(getTimezone());


        //将心跳信息缓存到redis中,在删除时判断设备是否在线
        System.out.println("心跳数据>>>>>>>>>" + devId + "心跳时间戳" + Long.toString(System.currentTimeMillis()));
        redisTemplate.boundHashOps(IOT_REDIS_PREFIX + devId).put("heartBeat", Long.toString(System.currentTimeMillis()));
        //判断设备是否被删除
        checkDeviceDel(devId);
        //判断设备是否在线
        checkStatus(devId);
        return deviceHeartBeatVO;
    }

    //判断设置是否被删除
    private void checkDeviceDel(String devId) {
        ThreadUtil.execute(() -> {
            //判断redis中时候有该key值,如果没有,就删除该设备,并通知设备已删除,如果有值,在判断
            if (Boolean.TRUE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + devId))) {
                String psk = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + devId).get("psk");
                String userId = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + devId).get("userId");
                if (StringUtils.isEmpty(psk) && StringUtils.isEmpty(userId)) {
                    // 向设备发送设备删除通知
                    sendDeviceDeleteNoticeToDevice(devId);
                    redisTemplate.delete(IOT_REDIS_PREFIX + devId);
                }
            } else {
                // 向设备发送设备删除通知
                sendDeviceDeleteNoticeToDevice(devId);
                redisTemplate.delete(IOT_REDIS_PREFIX + devId);
            }
        });
    }

    private void checkStatus(String devId) {
        ThreadUtil.execute(() -> {
            //判断设备是否有心跳信息和心跳状态
            if (Boolean.TRUE.equals(redisTemplate.hasKey(IOT_REDIS_PREFIX + devId))) {
                String status = (String) redisTemplate.boundHashOps(IOT_REDIS_PREFIX + devId).get("status");
                // 判断设备状态
                if (ObjectUtil.isNull(status) || StringUtils.equals(status, "offline")) {
                    redisTemplate.boundHashOps(IOT_REDIS_PREFIX + devId).put("status", "online");
                    Device device = getById(devId);
                    if (StringUtils.equals(device.getStatus(), "offline")) {
                        device.setStatus("online");
                        updateById(device);
                        deviceOnlineNotice("", devId);
                    }
                }
            }
        });
    }

    private String getTimezone() {
        // 获取系统默认时区
        TimeZone timeZone = TimeZone.getDefault();

        // 获取时区ID（例如：America/New_York）
        String id = timeZone.getID();

        // 获取时区的短名称（例如：EST或EDT，但不总是可靠）
        String standardName = timeZone.getDisplayName(false, TimeZone.SHORT, Locale.US);
        String daylightName = timeZone.getDisplayName(true, TimeZone.SHORT, Locale.US);

        // 获取原始偏移量（以秒为单位），然后转换为小时和分钟
        int rawOffset = timeZone.getRawOffset();
        int offsetHours = rawOffset / 3600;
        int offsetMinutes = Math.abs((rawOffset % 3600) / 60);

        // 格式化偏移量（例如：-0500）
        String offsetString = String.format("%+03d%02d", offsetHours, offsetMinutes);

        // 注意：下面的DST规则是非常简化的，并且可能不会总是正确
        // POSIX时区格式中的DST规则通常非常复杂
        // 这里只是为了演示如何构造一个基本的POSIX格式字符串

        // 检查是否处于夏令时
        boolean inDST = timeZone.inDaylightTime(new Date());

        // 构造简化的POSIX时区字符串
        // 例如：EST5EDT（这只是一个非常简化的示例，实际POSIX字符串可能更复杂）
        String posixTimezone = standardName + offsetString +
                (inDST ? daylightName.substring(1) : ""); // 去掉DST名称前的首字母（通常是'D'或'S'）

        // 打印POSIX时区字符串
        // 请注意，这个字符串可能并不完全符合POSIX时区格式的规范
        System.out.println("Simplified POSIX Time Zone: " + posixTimezone);

        return posixTimezone;
    }

    // authCode的长度为32字节，由16 bytes的二进制数据转成16进制字符串而成。
    private String getRandomCode() {
        // 使用SecureRandom生成16字节的随机数据
        byte[] randomBytes = new byte[16];
        new SecureRandom().nextBytes(randomBytes);

        // 将字节数组转换成16进制字符串
        StringBuilder hexString = new StringBuilder(32); // 16字节 * 2字符/字节 = 32字符
        for (byte b : randomBytes) {
            // 将每个字节转换成两位的16进制数并追加到StringBuilder中
            hexString.append(String.format("%02x", b & 0xff));
        }
        // 打印生成的authCode
        return hexString.toString();
    }

    @Override
    public List<DeviceInfoVO> getDeviceInfoList(List<String> deviceIdList) {
        List<Device> deviceList = listByIds(deviceIdList);
        List<DeviceInfoVO> deviceInfoVOList = new ArrayList<>();
        for (Device device : deviceList) {
            DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
            BeanUtil.copyProperties(device, deviceInfoVO);
            deviceInfoVOList.add(deviceInfoVO);
        }
        return deviceInfoVOList;
    }

    @Override
    public DeviceInfoVO getDeviceInfoById(String deviceId) {
        if (ObjectUtil.isNull(deviceId)) {
            throw new CommonException("设备id不能为空");
        }
        Device device = getById(deviceId);
        DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
        BeanUtil.copyProperties(device, deviceInfoVO);

        return deviceInfoVO;
    }

    private Boolean clearDeviceCache(String deviceId) {
        Boolean result1 = redisTemplate.delete(IOT_REDIS_PREFIX + deviceId);
        Boolean result2 = redisTemplate.delete(IOT_REDIS_PREFIX + "token:" + deviceId);
        return Boolean.TRUE.equals(result1) && Boolean.TRUE.equals(result2);
    }

    public Boolean isDeviceSnExist(String sn) {
        QueryWrapper<Device> deviceQueryWrapper = new QueryWrapper<>();
        deviceQueryWrapper.eq("sn", sn);
        deviceQueryWrapper.eq("delete_flag", "NOT_DELETE");
        List<Device> deviceList = list(deviceQueryWrapper);
        return ObjectUtil.isNotEmpty(deviceList);
    }

    public Boolean isDeviceIdExist(String deviceId) {
        Device device = getById(deviceId);
        return !Objects.isNull(device);
    }

    public void checkProjectId(String projectId) {
        if (ObjectUtil.isNull(projectId)) {
            throw new CommonException("项目id不能为空");
        }
        List<AppProjectVO> appProjects = projectApi.getAppProjects();
        List<String> appProjectIds = new ArrayList<>();
        for (AppProjectVO appProjectVO : appProjects) {
            appProjectIds.add(appProjectVO.getId());
        }
        if (!appProjectIds.contains(projectId)) {
            throw new CommonException("当前用户下没有该项目，项目id:" + projectId);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public String add(DeviceAddParam deviceAddParam) {

        Device device = BeanUtil.toBean(deviceAddParam, Device.class);
        // 生成deviceId
        String deviceId = UUID.randomUUID().toString();
        device.setId(deviceId);
        //添加到数据库中
        boolean save = save(device);
        if (Boolean.FALSE.equals(save)) {
            throw new CommonException("保存设备信息失败");
        }
        return deviceId;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void edit(DeviceEditParam deviceEditParam) {
        Device device = queryEntity(deviceEditParam.getId());
        BeanUtil.copyProperties(deviceEditParam, device);
        updateById(device);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<DeviceIdParam> deviceIdParamList) {
        // 执行删除
        removeByIds(CollStreamUtil.toList(deviceIdParamList, DeviceIdParam::getId));
    }

    @Override
    public String detail(DeviceControlDTO deviceControlDTO) {
        Device device = getById(deviceControlDTO.getDevId());
        if (ObjectUtil.isEmpty(device)) {
            throw new CommonException("设备表不存在，id值为：{}", deviceControlDTO.getDevId());
        }
        ArrayList<HashMap<String, Object>> service = new ArrayList<>();
        //向设备发送请求详情的命令
        String sid = "devNetInfo";
        String st = "devNetInfo";
        HashMap<String, Object> body = new HashMap<>();
        HashMap<String, String> data = new HashMap<>();
        body.put("st", st);
        body.put("data", data);
        service.add(body);

        CoapToHttpDTO coapToHttpDTO = new CoapToHttpDTO();
        coapToHttpDTO.setUrl(sid);
        coapToHttpDTO.setMethod("GET");
        coapToHttpDTO.setBody(JSON.toJSONString(service));
        HashMap<String, Object> option = new HashMap<>();
        option.put("REQ_ID", RandomUtil.randomString(10));
        option.put("DEV_ID", deviceControlDTO.getDevId());
        option.put("USER_ID", StpAppLoginUserUtil.getAppLoginUserId());
        option.put("SEQ_NUM_ID", RandomUtil.randomNumbers(5));
        coapToHttpDTO.setOption(option);
        // 获取的详情
        CoapToHttpVO coapToHttpVO = coapApi.sendCoapMessage(coapToHttpDTO);

        if (ObjectUtil.isNotNull(coapToHttpVO) && StringUtils.isNotBlank(coapToHttpVO.getBody())) {
            return coapToHttpVO.getBody();
        }
        return "";
    }

    @Override
    public Device queryEntity(String id) {
        Device device = getById(id);
        if (ObjectUtil.isEmpty(device)) {
            throw new CommonException("设备表不存在，id值为：{}", id);
        }
        //向设备发送请求详情的命令

        return device;
    }

}
